#include "parser.h"
#include "template.h"
#include "builtinfunction.h"
#include "parameterlist.h"
#include "parameter.h"
#include "var.h"
#include "component.h"
#include "type.h"
#include "getopts.h"
#include "errorlog.h"

Parser* Parser::s_parser = 0;

Parser::Parser(const QOptions& options)
        : Namespace( YYLTYPE() )
{
    s_parser = this;
    // TODO: These do not belong here

    if ( options.option(Cmd_Platform).value() == "wave" )
    {
        // Add the builtin functions
        ParameterList* params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("waveurl", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wavelet", "JSOT.wala.wavelet", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wave", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wavelets", "JSOT.wala.wavelets", params, Type(Type::List) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("id", Type(Type::Scalar), YYLTYPE() ) );
        params->appendChild( new Parameter("domain", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wave", "JSOT.wala.wave", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        appendChild( new BuiltinFunction("waves", "JSOT.wala.waves", params, Type(Type::List) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wavelet", Type(Type::Scalar), YYLTYPE() ) );
        params->appendChild( new Parameter("id", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("doc", "JSOT.wala.doc", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wavelet", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("docs", "JSOT.wala.docs", params, Type(Type::List) ) );
    }
    else if ( options.option(Cmd_Platform).value() == "simulation" )
    {
        // Add the builtin functions
        ParameterList* params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("waveurl", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wavelet", "wala.Simulation.wavelet", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wave", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wavelets", "wala.Simulation.wavelets", params, Type(Type::List) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("id", Type(Type::Scalar), YYLTYPE() ) );
        params->appendChild( new Parameter("domain", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("wave", "wala.Simulation.wave", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        appendChild( new BuiltinFunction("waves", "JSOT.wala.waves", params, Type(Type::List) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wavelet", Type(Type::Scalar), YYLTYPE() ) );
        params->appendChild( new Parameter("id", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("doc", "wala.Simulation.doc", params, Type(Type::Scalar) ) );

        // Add the builtin functions
        params = new ParameterList(YYLTYPE());
        params->appendChild( new Parameter("wavelet", Type(Type::Scalar), YYLTYPE() ) );
        appendChild( new BuiltinFunction("docs", "JSOT.wala.docs", params, Type(Type::List) ) );
    }
    else if ( options.option(Cmd_Platform).value() == "gadget" )
    {
    }
    else
    {
        ErrorLog::error(YYLTYPE(), "Unknown platform " + options.option(Cmd_Platform).value() );
        return;
    }

    // TODO END: These do not belong here

    // Add the builtin functions
    ParameterList* params = new ParameterList(YYLTYPE());
    // The len() function works for lists and scalars (at least strings).
    params->appendChild( new Parameter("arg", Type(Type::Invalid), YYLTYPE() ) );
    appendChild( new BuiltinFunction("len", "wala.len", params, Type(Type::Scalar) ) );

    // Add the builtin functions
    params = new ParameterList(YYLTYPE());
    // The len() function works for lists and scalars (at least strings).
    params->appendChild( new Parameter("node", Type(Type::Invalid), YYLTYPE() ) );
    appendChild( new BuiltinFunction("children", "wala.children", params, Type(Type::List) ) );

    // Add the builtin functions
    params = new ParameterList(YYLTYPE());
    params->appendChild( new Parameter("d", Type(Type::Scalar), YYLTYPE() ) );
    appendChild( new BuiltinFunction("text", "wala.text", params, Type(Type::Scalar) ) );

    // Add the builtin functions
    params = new ParameterList(YYLTYPE());
    params->appendChild( new Parameter("d", Type(Type::Scalar), YYLTYPE() ) );
    appendChild( new BuiltinFunction("attributes", "wala.attributes", params, Type(Type::Scalar) ) );

    // Add the builtin functions
    params = new ParameterList(YYLTYPE());
    params->appendChild( new Parameter("d", Type(Type::Scalar), YYLTYPE() ) );
    params->appendChild( new Parameter("attrib", Type(Type::Scalar), YYLTYPE() ) );
    appendChild( new BuiltinFunction("localAttribute", "wala.localAttribute", params, Type(Type::Scalar) ) );

    // Add the builtin functions
    params = new ParameterList(YYLTYPE());
    params->appendChild( new Parameter("d", Type(Type::Scalar), YYLTYPE() ) );
    params->appendChild( new Parameter("attrib", Type(Type::Scalar), YYLTYPE() ) );
    params->appendChild( new Parameter("value", Type(Type::Scalar), YYLTYPE() ) );
    appendChild( new BuiltinFunction("setLocalAttribute", "wala.setLocalAttribute", params, Type(Type::Invalid) ) );
}

Node* Parser::getEntry( const QString& name )
{
    Node* p = this->firstChild();
    while( p )
    {
        Template* t = dynamic_cast<Template*>(p);
        if ( t )
        {
            if ( t->name() == name )
                return t;
        }
        else if ( dynamic_cast<Component*>(p) )
        {
            Component* c = dynamic_cast<Component*>(p);
            if ( c->name() == name )
                return c;
        }
        else
        {
            Function* f = dynamic_cast<Function*>(p);
            if ( f )
            {
                if ( f->name() == name )
                    return f;
            }
            else
            {
                Var* v = dynamic_cast<Var*>(p);
                if ( v )
                {
                    if ( v->name() == name )
                        return v;
                }
            }
        }
        p = p->nextSibling();
    }
    return 0;
}

QList<Template*> Parser::templates() const
{
    QList<Template*> result;
    Node* p = this->firstChild();
    while( p )
    {
        Template* t = dynamic_cast<Template*>(p);
        if ( t )
            result.append(t);
        p = p->nextSibling();
    }

    return result;
}

Template* Parser::mainTemplate()
{
    return dynamic_cast<Template*>( getEntry("main") );
}

QList<Var*> Parser::variables() const
{
    QList<Var*> result;
    Node* p = this->firstChild();
    while( p )
    {
        Var* v = dynamic_cast<Var*>(p);
        if ( v )
            result.append(v);
        p = p->nextSibling();
    }

    return result;
}

QList<UserFunction*> Parser::userFunctions() const
{
    QList<UserFunction*> result;
    Node* p = this->firstChild();
    while( p )
    {
        UserFunction* f = dynamic_cast<UserFunction*>(p);
        if ( f )
            result.append(f);
        p = p->nextSibling();
    }

    return result;
}

QList<Component*> Parser::components() const
{
    QList<Component*> result;
    Node* p = this->firstChild();
    while( p )
    {
        Component* c = dynamic_cast<Component*>(p);
        if ( c )
            result.append(c);
        p = p->nextSibling();
    }

    return result;
}
